﻿namespace Mp3Observer.Common.Behaviors
{
    #region usings

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Threading;

    #endregion

    /// <summary>
    /// Helper functions for ItemsControl: ScrollToLastItem scroll to the last item when ItemContainerGenerator.StatusChanged is called.
    /// </summary>
    /// <example>
    /// <code>
    ///   <ListBox ItemsSource="{Binding...}" behaviors:ItemsControlHelper.ScrollToLastItem="True"
    /// </code>
    /// </example>
    /// <remarks>
    /// be careful with virtualization! test!!
    /// </remarks>
    public class ItemsControlHelper
    {
        /// <summary>
        /// ScrollToLastItem DependencyProperty enabled binding.
        /// </summary>
        public static readonly DependencyProperty ScrollToLastItemProperty = DependencyProperty.RegisterAttached("ScrollToLastItem",
           typeof(bool), typeof(ItemsControlHelper), new FrameworkPropertyMetadata(false, OnScrollToLastItemChanged));

        /// <summary>
        /// Sets the scroll to last item.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="value">if set to <c>true</c> [value].</param>
        public static void SetScrollToLastItem(UIElement sender, bool value)
        {
            sender.SetValue(ScrollToLastItemProperty, value);
        }

        /// <summary>
        /// Gets the scroll to last item.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <returns></returns>
        public static bool GetScrollToLastItem(UIElement sender)
        {
            return (bool)sender.GetValue(ScrollToLastItemProperty);
        }

        /// <summary>
        /// Called when [scroll to last item changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnScrollToLastItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var itemsControl = sender as ItemsControl;
            if (itemsControl != null)
                itemsControl.ItemContainerGenerator.StatusChanged += (s, a) => OnItemsChanged(itemsControl, s, a);
        }

        /// <summary>
        /// Called when [items changed].
        /// </summary>
        /// <param name="itemsControl">The items control.</param>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private static void OnItemsChanged(ItemsControl itemsControl, object sender, EventArgs e)
        {
            var generator = sender as ItemContainerGenerator;
            if (generator.Status == GeneratorStatus.ContainersGenerated)
                if (itemsControl.Items.Count > 0)
                    ScrollIntoView(itemsControl, itemsControl.Items[itemsControl.Items.Count - 1]);
        }

        /// <summary>
        /// Scrolls into view of the itemscontrol
        /// </summary>
        /// <param name="itemsControl">The items control.</param>
        /// <param name="item">The item.</param>
        private static void ScrollIntoView(ItemsControl itemsControl, object item)
        {
            if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                OnBringItemIntoView(itemsControl, item);
            else
            {
                Func<object, object> onBringIntoView = (o) => OnBringItemIntoView(itemsControl, item);
                itemsControl.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(onBringIntoView));
            }
        }

        /// <summary>
        /// Calles BringIntoView of the FrameworkElement.
        /// </summary>
        /// <param name="itemsControl">The items control.</param>
        /// <param name="item">The item to bring into view.</param>
        /// <returns>null</returns>
        private static object OnBringItemIntoView(ItemsControl itemsControl, object item)
        {
            var element = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
            if (element != null)
                element.BringIntoView();
            return null;
        }
    }
}
